package zdream.pmfield.stage.effect;

import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

import zdream.pmfield.data.foe.MoveAdditionalEffects;
import zdream.pmfield.stage.director.AttackContext;
import zdream.pmfield.stage.director.FixPair;
import zdream.pmfield.stage.director.HookStore;
import zdream.pmfield.stage.director.HookType;
import zdream.pmfield.stage.director.Hooks;
import zdream.pmfield.stage.object.FoeActor;

import static zdream.pmfield.data.foe.MoveAdditionalEffects.E_FIXED_BUFF;

/**
 * @author Zdream
 * @date 2023-12-08
 * @since 0.0.1
 * @see TypeFixedRateEffect
 */
public class BuffFixedRateEffect extends AbstractEffect {
	private static final String DESC_KEY_ATTACKER_BUFFS = MoveAdditionalEffects.K_FIXED_BUFF_ATTACKER_BUFFS;
	private static final String DESC_KEY_DEFENSE_BUFFS = MoveAdditionalEffects.K_FIXED_BUFF_DEFENSE_BUFFS;
	private static final String DESC_KEY_TYPE = MoveAdditionalEffects.K_FIXED_BUFF_TYPE;
	private static final String DESC_KEY_RATE = MoveAdditionalEffects.K_FIXED_BUFF_RATE;

	private final String[] attackerBuffs;
	private final String[] defenseBuffs;
	private final EffectType type;
	private final byte level;

	public BuffFixedRateEffect(MoveAdditionalEffects.EffectDescription description) {
		super(E_FIXED_BUFF);

		final Map<String, ?> parsed = MoveAdditionalEffects.parse(description);
		Objects.requireNonNull(parsed);

		this.attackerBuffs = (String[]) parsed.get(DESC_KEY_ATTACKER_BUFFS);
		this.defenseBuffs = (String[]) parsed.get(DESC_KEY_DEFENSE_BUFFS);
		this.type = EffectType.valueOf((String) parsed.get(DESC_KEY_TYPE));
		this.level = ((Integer) parsed.get(DESC_KEY_RATE)).byteValue();

		Objects.requireNonNull(this.attackerBuffs);
		Objects.requireNonNull(this.defenseBuffs);
	}

	@Override
	public void init(Function<HookType, HookStore.HookHandlerBuilder> onHook) {
		onHook.apply(HookType.ON_CHECK_TYPE_FIXED)
				.handle(this::applyEffect)
				.withPriority(-99)
				.buildAndSave();
	}

	private void applyEffect(Hooks.HookContext rawContext) {
		final Hooks.AttackHookContext hooks = rawContext.as();
		final AttackContext attackContext = hooks.attackContext;

		// check attacker buffs
		if (attackerBuffs.length != 0) {
			final FoeActor attacker = attackContext.attacker;

			if (!Arrays.stream(attackerBuffs).allMatch(attacker::hasBuff)) {
				return;
			}
		}

		for (AttackContext.AttackContextItem item : attackContext.items) {
			final FoeActor targetFoe = item.targetFoe;
			if (targetFoe == null) {
				continue;
			}

			// check defense buffs
			if (defenseBuffs.length != 0) {
				if (!Arrays.stream(defenseBuffs).allMatch(targetFoe::hasBuff)) {
					continue;
				}
			}

			// update fixed rate
			if (type == EffectType.cover) {
				item.typeFixPairs.removeIf(FixPair::isForType);
			}
			item.typeFixPairs.add(FixPair.of(attackerBuffs, defenseBuffs, level));
		}
	}

	private enum EffectType {
		append,
		cover,
	}
}
